/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "AdtChunkFile.h"

namespace PQGame
{
	AdtChunkFile::~AdtChunkFile(void)
	{
	}

	AdtChunkFile::AdtChunkFile(PQEngine::FileBase* file,int index,adt_mcin *info)
	{
		layerCount=0;
		textureSizes[0]=0;
		textureSizes[1]=0;
		textureSizes[2]=0;
		textureSizes[3]=0;
		
		blendMap=new unsigned char[64*64*4];
		shadowMap=new unsigned char[64*64];
		
		init(file,info);
	}

	void AdtChunkFile::init(PQEngine::FileBase* f,adt_mcin *info)
	{
		int offset=info->offset;
		int size=info->size;
		if(offset==0||size==0){
			return;
		}
		char fourcc[5];
		f->seek(offset);
		f->read(fourcc,4); // MCNK
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;

		if (strncmp(fourcc, "MCNK", 4)!=0 || size == 0) {
			//main chunk error
			return;
		}
		
		chunkHeaderPos=f->getPosition();

		header=new MapChunkHeader;
		f->read(header,sizeof(MapChunkHeader));

		readVerticesHeight(f,header);
		readVerticesNormal(f,header);
		readLayerInfo(f,header);
		readLayerAlphaMap(f,header);
		readLayerShadowMap(f,header);
	}

	bool AdtChunkFile::readLayerShadowMap(PQEngine::FileBase *f,MapChunkHeader *header)
	{
		char fourcc[5];
		int size;
		int mcsh_pos=chunkHeaderPos+header->ofsShadow;
		f->seek(mcsh_pos-8);
		f->read(fourcc,4); // MCSH
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;
		
		if (strncmp(fourcc, "MCSH", 4) == 0) {
			//The shadows are stored per bit, not byte as 0 or 1 (off or on)
			//So we have 8 bytes (which equates to 64 values) X 64 bytes (64 values in this case) 
			//Which ends up as a square 64x64 shadowmap with either white or black.
			//Note that the shadow values come LSB first.

			unsigned char *p, c[8];
			p = shadowMap;
			for (int j=0; j<64; j++) {
				f->read(c,8);
				for (int i=0; i<8; i++) {
					for (int b=0x01; b!=0x100; b<<=1) {
						*p++ = (c[i] & b) ? 85 : 0;
					}
				}
			}

			for (int i=0; i<64*64; i++) {
				blendMap[i*4+3] = shadowMap[i];
			}
			return true;
			
		}else{
			return false;
		}
	}

	bool AdtChunkFile::readLayerInfo(PQEngine::FileBase *f,MapChunkHeader *header)
	{
		char fourcc[5];
		int size;
		int mcly_pos=chunkHeaderPos+header->ofsLayer;
		f->seek(mcly_pos-8);
		f->read(fourcc,4); // MCLY
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;
		int cnt=(int)size/16;
		layerCount=cnt;
		layersInfo=new map_chunk_layer[cnt];
		if (strncmp(fourcc, "MCLY", 4) == 0) {
			f->read(layersInfo, size);

			for(int i=1; i<cnt; i++) {
				if ((layersInfo[i].flags & MCLY_USE_ALPHAMAP) == 0) {
					textureSizes[i] = 0;
					continue;
				}
				if (i < 3 && (layersInfo[i+1].flags & MCLY_USE_ALPHAMAP)){
					textureSizes[i] = layersInfo[i+1].offsetInMCAL- layersInfo[i].offsetInMCAL;
				}else{
					textureSizes[i] = header->sizeAlpha - layersInfo[i].offsetInMCAL - 8;
				}
			}

			return true;
		}else{
			return false;
		}
	}

	bool AdtChunkFile::readVerticesHeight(PQEngine::FileBase *f,MapChunkHeader *header)
	{
		char fourcc[5];
		int size;
		int mcvt_pos=chunkHeaderPos+header->ofsHeight;
		f->seek(mcvt_pos-8);//minus 8 is for fourcc and size
		f->read(fourcc,4); // MCNK
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;
		if (strncmp(fourcc, "MCVT", 4) == 0) {
			heights=new float[9*9+8*8];
			f->read(heights,(9*9+8*8)*4);
			return true;
		}else{
			return false;
		}
	}

	bool AdtChunkFile::readVerticesNormal(PQEngine::FileBase *f,MapChunkHeader *header)
	{
		char fourcc[5];
		int size;
		int mcnr_pos=chunkHeaderPos+header->ofsNormal;
		f->seek(mcnr_pos-8);
		f->read(fourcc,4); // MCNK
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;
		if (strncmp(fourcc, "MCNR", 4) == 0) {
			normals=new map_chunk_normal[9*9+8*8];
			f->read(normals,(9*9+8*8)*3);
			return true;
		}else{
			return false;
		}
	}

	bool AdtChunkFile::readLayerAlphaMap(PQEngine::FileBase *f,MapChunkHeader *header)
	{
		char fourcc[5];
		int size;
		int mcal_pos=chunkHeaderPos+header->ofsAlpha;
		f->seek(mcal_pos-8);
		f->read(fourcc,4); // MCAL
		f->read(&size, 4);
		flipcc(fourcc);
		fourcc[4] = 0;
		
		if (strncmp(fourcc, "MCAL", 4) == 0) {
			genAlphaMap(f);
			return true;
		}else{
			return false;
		}
	}

	void AdtChunkFile::genAlphaMap(PQEngine::FileBase *f)
	{
		unsigned char *data = (unsigned char *)f->getBuffer();
		for(int i=1;i<layerCount;i++){
			if((layersInfo[i].flags&MCLY_USE_ALPHAMAP)==0){
				continue;
			}
			unsigned char *alphaBuf = data + layersInfo[i].offsetInMCAL;
			unsigned char *alphaMap= new unsigned char[64*64];
			alphaMaps[i-1]=alphaMap;
			if (layersInfo[i].flags&MCLY_ALPHAMAP_COMPRESS) {
				unsigned int in = 0;
				unsigned int out = 0;
				while(out < 64*64){
					bool fill = (alphaBuf[in] & 0x80) > 0;
					unsigned int n = alphaBuf[in] & 0x7F;
					in++;
					for( unsigned int k = 0; k < n; k++ ){
						if (out >= 64*64)
							break;
						alphaMap[out] = alphaBuf[in];
						out++;
						if (!fill) in++;
					}
					if (fill){
						in++;
					}
				}
			} else {
				if (textureSizes[i] == 4096) {
					memcpy(alphaMap, alphaBuf, 4096);
				} else {
					unsigned char *p;
					p = alphaMap;
					for (int j=0; j<64; j++) {
						for(int k=0; k<32; k++) {
							unsigned char c = *alphaBuf++;
							*p++ = (c & 0x0f) << 4;
							*p++ = (c & 0xf0);
						}
					}
				}
			}

			for (int j=0; j<64*64; j++) {
				blendMap[j*4+i-1] = alphaMap[j];
			}		
		}
	}	
}
